说起十进制转为二进制,我想大家对它的算法并不陌生。
它在数学中的主要思想就是:用这个要转化的数不停的除2取余,并且用商来重复之前的操作,直至商为0;
那么,我们就可以根据这个思想来写出我们的C程序。
首先,我们必须要清楚的是:我们依次得到的这个余数其实是这个数二进制序列按从最低位到最高位依次得到的数。(这个不要搞错了,不然输出就可能会出问题的)
那么,我们可以创建一个数组将它存起来,然后再打印。也可以不用存储,直接打印。(下边代码是第一种情况)
int main()
{
int arr[32]; //一个整形变量是4个字节,也就是32位
int num;
int i=0;
printf("请输入一个整数:");
scanf("%d", &num);
while (num != 0)
{
arr[i] = num % 2;
num = num / 2;
i++;
}
i--; //解除最后一次循环i的自增
while (i+1!=0)//+1是因为防止arr[0]无法输出
{
printf("%d",arr[i]);
i--;
}
return 0;
}
我们很容易的可以发现,其实这个代码只能得到正数的二进制序列,而无法得到负数的二进制序列。
那么我们应该怎么办?
首先,我们先说一下整数在计算机里边的存储方式:
一个整数在计算机中占了4个字节,也就是32位。并且是以其二进制的补码形式存储的,并且第一位是其符号位(0代表正数,1代表负数)。
那补码又是什么呢?
一个二进制数有相应的原码,反码,补码和其对应。正数的原反补都相同。负数的反码等于其原码除了符号位以外的其他位的相反数(这里定义:1的相反数是0,0的相反数为1),符号位不变。而补码就等于其反码+1。
eg:15
原码:00000000 00000000 00000000 00001111
反码:00000000 00000000 00000000 00001111
补码:00000000 00000000 00000000 00001111
eg:-1
原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111
那么,我们在这里引入两个符号:"<<"(左移),">>"(右移)。
那这两个符号是干什么的呢?
它可以用来操控这个二进制数列的左右移动
eg:00000000 00000000 00000000 00001111(右移一位)
(0)0000000 00000000 00000000 00000111(1)(负数高位补1,正数补0)
那么,我们这样是不是就很容易得到它的二进制位,并且解决了之前不能获得负数二进制位的问题。
但是,我们在右移之后,虽然可以将这一位分离出来,但是就会丢失。那我们怎么来保存呢?
eg:0111&0001=0001;
1110&0001=0000;
通过上面的例子,我们是否发现,当我们用一个数和1相与的时候,就会得到它的当前最低位。
这样,我们就可以先与运算,然后右移一位。通过32次循环就可以做到了。
int main()
{
int arr[32]; //一个整形变量是4个字节,也就是32位
int num;
int i=0;
printf("请输入一个整数:");
scanf("%d", &num);
for (i = 0; i < 32; i++)
{
arr[31-i] = num&1;
num = num>>1;
}
for (i = 0; i < 32; i++)
{
printf("%d",arr[i]);
}
return 0;
}
虽然,这样是可以做。但是我们发现不管是哪个数字,它都需要循环32次来解决。是不是效率不是很高呢?
假如,我现在只想得到这个数的二进制序列中1的数量,那还用上边的方法一次一次循环判断吗?是不是太麻烦。的确是的。
那么来看这么一个公式:n=n&(n-1);
那么这个公式到底是干什么的呢?
eg:n= 1111; n= 0110;
n-1= 1110; n-1= 0101;
n&(n-1)=1110; n&(n-1)=0100;
是不是发现这个公式每次都会将n中最低位的1用0来替换。
那么我们是不是就可以利用这个公式,它每替换一个1,我们的计数器就加1;这样就可以大大的减少循环次数。而且不用开辟数组的内存。既节省了空间,也提高了效率。
int main()
{
int num;
int count=0;
printf("请输入一个整数:");
scanf("%d", &num);
while (num!=0)
{
num = num&(num - 1);
count++;
}
printf("%d\n",count);
return 0;
}
是不是看起来很简单,很方便呢?
当我们掌握了这个公式的使用之后。如果我们要判断一个数是否为2的n次幂(n为正整数)就很简单了。
我们只需要将上边的程序稍作修改。因为如果这个数是2的n次幂,那么其二进制序列中只有一个1了。
int main()
{
int num;
int count=0;
printf("请输入一个整数:");
scanf("%d", &num);
while (num!=0)
{
num = num&(num - 1);
count++;
}
if (count == 1)
printf("是\n");
else
printf("不是");
return 0;
}
当然,上边的所有程序都可以使用函数来实现。这样会更加的直观。
可以自己试试哦。